using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using System.Xml.Linq;

namespace ShipkaDefenders.Gameplay
{
    public enum GameStatus { Running, Paused, FastForward }
    public class GameEngine : Screens.Screen
    {
        public GameEngine(AnaLogic game)
        {
            g = game;
            Map = new Campaign.Level1();
            Map.LoadTextures(g);

            Scroll = Vector2.Zero;
            Scale = 1.0f;
            gametime = g.Elapsed;
            gamestart = g.Elapsed;
            Status = GameStatus.Running;
            Allies = Map.LoadBanners(g);
            Enemies = new List<Enemy>();
            Hud = new HUD(g);
            wmanager = new WaveManager(this);

            Pass = 10;
            Coins = 100;

            deb = string.Empty;
        }

        public Vector2 Scroll;
        public float Scale;
        private TimeSpan gametime;
        private TimeSpan gamestart;
        public GameStatus Status;

        public Random Rand = new Random();
        public List<Ally> Allies;
        public List<Enemy> Enemies;
        public Campaign.Level Map;
        public HUD Hud;
        private WaveManager wmanager;

        public List<int> DelEnemies = new List<int>();
        public List<int> DelAllies = new List<int>();

        public int Pass;
        public int Coins;

        public string deb;
        protected TimeSpan msgBoxOpen;
        protected string msgBoxText;

        public void MessageBox(string Text)
        {
            msgBoxOpen = GetTime();
            msgBoxText = Text;
        }

        public bool BuildAlly(int cue, int cl)
        {

            Dictionary<string, int> costs = new Dictionary<string, int>();
            costs.Add("russian", 60);
            costs.Add("russian_sniper", 80);
            string className = string.Empty;
            switch (cl)
            {
                case 1: className = "russian"; break;
                case 2: className = "russian_sniper"; break;
                default:
                    // NYI Player Class!
                    SelectNoAlly();
                    return false;
            }
            if (Coins >= costs[className])
            {
                Allies[cue].Build(className);
                Coins -= costs[className];
                return true;
            }
            else MessageBox("You can't afford this unit!");
            return false;
        }
        public override void Gestures(TimeSpan gt, GestureSample gs)
        {
            deb = string.Empty;
            switch (gs.GestureType)
            {
                case GestureType.FreeDrag:
                    Scroll += gs.Delta;
                    break;
                case GestureType.DoubleTap:
                    break;
                case GestureType.Pinch:
                    float scaleFactor = PinchZoom.GetScaleFactor(gs.Position, gs.Position2, gs.Delta, gs.Delta2);
                    Vector2 panDelta = PinchZoom.GetTranslationDelta(gs.Position, gs.Position2, gs.Delta, gs.Delta2, Scroll, scaleFactor);
                    float olds = Scale;
                    Scale *= scaleFactor;
                    if (Scale < 1.0f) Scale = 1.0f;
                    else if (Scale > 1.5f) Scale = 1.5f;
                    if (Scale != olds) Scroll += panDelta;
                    break;
                case GestureType.Tap:
                    if (!Hud.Tap(gs.Position))
                    {
                        SelectionState Sel = SelectionState.None;
                        foreach (Ally a in Allies)
                        {
                            SelectionState ss = a.Tap(gs.Position);
                            if (ss != SelectionState.None) { Sel = ss; }
                        }
                        switch (Sel)
                        {
                            case SelectionState.None:
                                SelectNoAlly();
                                Hud.SelNone();
                                break;
                            case SelectionState.Banner:
                                Hud.SelBanner();
                                break;
                            case SelectionState.Confirm:
                                if (BuildAlly(SelectedAlly(), Hud.SelectedUnitType))
                                    Hud.SelUnit();
                                break;
                            case SelectionState.Unit:
                                Hud.SelUnit();
                                break;
                        }
                    }
                    break;
            }
            if (Scroll.X > 0) Scroll.X = 0;
            if (Scroll.Y > 0) Scroll.Y = 0;
            float BoundX = (-1) * ((g.MapTextures[Map.MiBackground].Width * Scale) - g.GraphicsDevice.Viewport.Width);
            float BoundY = (-1) * ((g.MapTextures[Map.MiBackground].Height * Scale) - g.GraphicsDevice.Viewport.Height);
            if (Scroll.X < BoundX) Scroll.X = BoundX;
            if (Scroll.Y < BoundY) Scroll.Y = BoundY;
        }
        public override void Update(TimeSpan gamet, TimeSpan elapsed)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                g.ChangeState(ScreenState.Menu);

            foreach (int e in DelEnemies) { if (Enemies.Count > e) Enemies.RemoveAt(e); }
            foreach (int e in DelAllies) { Allies[e] = new Ally(g, e); }
            DelEnemies.Clear();
            DelAllies.Clear();

            wmanager.Update();

            for (int e = 0; e < Enemies.Count; e++)
            {
                Enemies[e].Update(gametime, elapsed);
                Enemies[e].UpdateFight((from a in Allies where (a.State == EState.Fight || a.State == EState.Idle) select a).ToList());
                Enemies[e].Index = e;
            }
            foreach (Ally a in Allies)
            {
                a.Update(gametime, elapsed);
                a.UpdateFight(Enemies);
            }
            Hud.Update(gametime);

            gametime += UpdateTime(elapsed);
        }
        public override void Draw(SpriteBatch sb)
        {
            sb.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
            Drw(Map.MiBackground, Vector2.Zero);
            foreach (Ally a in Allies)
            {
                a.DrawSelf();
            }
            foreach (Enemy e in Enemies)
            {
                e.DrawSelf();
            }
            Drw(Map.MiForeground, Vector2.Zero);
            Hud.DrawSelf();
            if (Status == GameStatus.Paused)
            {
                g.spriteBatch.Draw(g.MapTextures["box"], new Rectangle(0, 0, 800, 480), new Color(0, 0, 0, 20));
                g.spriteBatch.Draw(g.MapTextures["box"], new Rectangle(240, 230, 330, 40), new Color(0, 0, 0, 150));
                if ((g.Elapsed.Milliseconds / 2) < 250)
                    g.WriteString("Calibri", 18, "Paused", new Vector2(366, 234), Color.White);
            }
            double msgb = (GetTime() - msgBoxOpen).TotalMilliseconds;
            if (msgb < 3000 && msgBoxText != null)
            {
                int alpha = (int)MathHelper.Clamp(((((int)msgb - 1200) / 1800) * 255), 0, 255);
                g.WriteString("Calibri", 18, msgBoxText, new Vector2(((800 - g.MapFonts["Calibri18"].MeasureString(msgBoxText).X) / 2), 380), new Color(255, 255, 255, alpha));
            }
            g.WriteString("Segoe", 16, deb);
        }
        // ---
        public AnaLogic GetGameClass()
        {
            return g;
        }
        public TimeSpan GetGameplayTime()
        {
            return (GetTime() - GetStart());
        }
        public TimeSpan GetTime()
        {
            return gametime;
        }
        public TimeSpan GetStart()
        {
            return gamestart;
        }
        public TimeSpan UpdateTime() { return UpdateTime(g.UpdateTime); }
        public TimeSpan UpdateTime(TimeSpan elapsed)
        {
            switch (Status)
            {
                case GameStatus.Running:
                    return elapsed;
                case GameStatus.FastForward:
                    return TimeSpan.FromMilliseconds(elapsed.TotalMilliseconds * 2);
                default:
                    return TimeSpan.Zero;
            }
        }
        public float GameSpeed()
        {
            return (float)UpdateTime(TimeSpan.FromSeconds(1)).Seconds;
        }
        public void SelectNoAlly()
        {
            foreach (Ally a in Allies)
            {
                a.Selected = false;
                if (a.State == EState.Banner_Anim)
                    a.State = EState.Banner;
            }
            Hud.SelNone();
        }
        public int SelectedAlly()
        {
            for (int a = 0; a < Allies.Count; a++)
            {
                if (Allies[a].Selected) return a;
            }
            return -1;
        }
        public Ally SelectedBanner()
        {
            foreach (Ally a in Allies)
            {
                if (a.Selected) return a;
            }
            return null;
        }
        public HUDButton SelectedHUDButton()
        {
            foreach (HUDButton hb in Hud.Buttons)
            {
                if (hb.Selected) return hb;
            }
            return null;
        }
        // ---
        public void Drw(string t, Vector2 p, float alpha)
        {
            Drw(t, p, -1, alpha, SpriteEffects.None);
        }
        public void Drw(string t, Vector2 p)
        {
            Drw(t, p, -1);
        }
        public void Drw(string t, Vector2 p, int f)
        {
            Drw(t, p, f, 255, SpriteEffects.None);
        }
        public void Drw(string t, Vector2 p, int f, bool fh)
        {
            Drw(t, p, f, 255, (fh ? SpriteEffects.FlipHorizontally : SpriteEffects.None));
        }
        public void Drw(string t, Vector2 p, int f, float alpha, SpriteEffects se)
        {
            Rectangle r = new Rectangle();
            Texture2D tx = g.MapTextures[t];
            r.X = (int)(p.X + Scroll.X);
            r.Y = (int)(p.Y + Scroll.Y);
            r.Width = (int)(tx.Width * Scale);
            r.Height = (int)(tx.Height * Scale);
            if (f < 0)
            {
                g.spriteBatch.Draw(tx, r, new Color(255, 255, 255, alpha));
            }
            else
            {
                r.Width = r.Height;
                g.spriteBatch.Draw(tx, r, new Rectangle((f * tx.Height), 0, tx.Height, tx.Height), new Color(255, 255, 255, alpha), 0, Vector2.Zero, se, 0.0f);
            }
        }
        // ---
        public Vector2 Abs(Vector2 i)
        {
            Vector2 a = new Vector2(Math.Abs(Scroll.X), Math.Abs(Scroll.Y));
            return i + a;
        }
        public Vector2 Sce(Vector2 i)
        {
            Vector2 bg = new Vector2(g.MapTextures[Map.MiBackground].Width, g.MapTextures[Map.MiBackground].Height);
            return (i * ((bg * Scale) / bg));
        }
    }
    public static class PinchZoom
    {   // this class' been copy-pasted! but i don't remember from who
        public static float GetScaleFactor(Vector2 position1, Vector2 position2, Vector2 delta1, Vector2 delta2)
        {
            Vector2 oldPosition1 = position1 - delta1;
            Vector2 oldPosition2 = position2 - delta2;
            float distance = Vector2.Distance(position1, position2);
            float oldDistance = Vector2.Distance(oldPosition1, oldPosition2);
            if (oldDistance == 0 || distance == 0)
            {
                return 1.0f;
            }
            return distance / oldDistance;
        }
        public static Vector2 GetTranslationDelta(Vector2 position1, Vector2 position2, Vector2 delta1, Vector2 delta2, Vector2 objectPos, float scaleFactor)
        {
            Vector2 oldPosition1 = position1 - delta1;
            Vector2 oldPosition2 = position2 - delta2;
            Vector2 newPos1 = position1 + (objectPos - oldPosition1) * scaleFactor;
            Vector2 newPos2 = position2 + (objectPos - oldPosition2) * scaleFactor;
            Vector2 newPos = (newPos1 + newPos2) / 2;
            return newPos - objectPos;
        }
    }
    public class MotionPath
    {
        public List<MotionPathKey> Keys = new List<MotionPathKey>();
        protected GameEngine g;
        public MotionPath(GameEngine game)
        {
            g = game;
        }
        public Vector2 Evaluate(float t)
        {
            MotionPathKey cmpk = null;
            MotionPathKey nmpk = null;
            foreach (MotionPathKey mk in Keys)
            {
                if (mk.Time > t)
                {
                    nmpk = mk;
                    break;
                }
                cmpk = mk;
            }
            if (cmpk == null) cmpk = Keys[0];
            if (nmpk == null || cmpk == null) return Vector2.Zero;
            Vector2 rel = (nmpk.Position - cmpk.Position);
            float k = ((t - cmpk.Time) / (nmpk.Time - cmpk.Time));
            if (float.IsNegativeInfinity(k)) k = 0;
            Vector2 r = (cmpk.Position + (rel * k)) * g.Scale;
            return r;
        }
    }
    public class MotionPathKey
    {
        public Vector2 Position;
        public float Time;
        public MotionPathKey(float t, Vector2 p)
        {
            Position = p;
            Time = t;
        }
    }
}
